#include <stdio.h>
#include "params.h"
#include "types.h"
#include "defs.h"
#include "cpu.h"
#include "mem.h"
#include "err.h"

byte RV32_ALIGNED memory[MEM_PG_NUM * PG_SIZE + sizeof (int32)];


int 
mem_phy_read (uint32 addr, int32 * buf, int mode) {
    int stat;
    stat = 0;
    if (addr >= MEM_SIZE) {
        stat |= E_MEM_INV_ADDR;
        goto error;
    }
    if (mode == RW_DWORD) {
        *buf = *((int32*)(memory + addr));
        goto success;
    } else if (mode == RW_WORD) {
        *buf = *((int16*)(memory + addr));
        goto success;
    } else if (mode == RW_BYTE) {
        *buf = *((byte*)(memory + addr));
        goto success;
    } else {
        stat |= E_MEM_INV_MODE;
        goto error;
    }
    success:
    return stat;
    error:
    return stat;
}

int 
mem_phy_write (uint32 addr, int32 value, int mode) {
    int stat;
    stat = 0;
    if (addr >= MEM_SIZE) {
        stat |= E_MEM_INV_ADDR;
        goto error;
    }
    if (mode == RW_DWORD) {
        *((int32*)(memory + addr)) = value;
        goto success;
    } else if (mode == RW_WORD) {
        *((int16*)(memory + addr)) = (int16)value;
        goto success;
    } else if (mode == RW_BYTE) {
        *((byte*)(memory + addr)) = (byte)value;
        goto success;
    } else {
        stat |= E_MEM_INV_MODE;
        goto error;
    }
    success:
    return stat;
    error:
    return stat;
}

/* bv32 分页方案 
 * L1PT = PGTADDR
 * VA:    | VPN.0 [31:22] | VPN.1 [21:12] |   OFFSET [11:0]  |
 * PTE:   | PPN.0 [31:22] | PPN.1 [21:12] |   FLAGS  [11:0]  |
 * L2PT = |  L1PT[VA.VPN.0].PPN [31:12]   |   000000000000   |
 * PA  =  |  L2PT[VA.VPN.1].PPN [31:12]   | VA.OFFSET [11:0] |
 * 二级页表,每个页表4K,页表项4B. 每个页表有1024个页表项.
 */

int
bv32_convert (uint32 va, uint32 pgt_addr, uint32* pa, uint32* flags) {
    int stat;
    uint32 * l1_pte_list, * l2_pte_list;
    uint32 pte, pte_off;
    uint32 phy_addr;
    stat = 0;
    if (pgt_addr + (~PG4K_MASK) >= MEM_SIZE) {
        stat |= E_MEM_INV_ADDR;
        goto error;
    }
    if ((pgt_addr & (~PG4K_MASK)) != 0) {
        stat |= E_MEM_BV32_PGT_NOT_ALIGNED;
        goto error;
    }
    // uint32 offset, uint32*1 = byte*4
    l1_pte_list = (uint32*)(memory + pgt_addr);
    pte = l1_pte_list[L1_PTE_INDEX(va)];
    if (!(pte & PTE_V)) {
        stat |= E_MEM_BV32_PTE_MISSING_V;
        goto error;
    }
    l2_pte_list = (uint32*)(memory + (pte & PG4K_MASK));
    pte = l2_pte_list[L2_PTE_INDEX(va)];
    if (!(pte & PTE_V)) {
        stat |= E_MEM_BV32_PTE_MISSING_V;
        goto error;
    }
    phy_addr = (pte&PG4K_MASK|PADDR_OFFSET(va));
    *pa = phy_addr;
    *flags = pte & FLAG_MASK;
    return stat;
    error:
    return stat;
}

int 
bv32_mem_virt_read (uint32 addr, uint32 pgt_addr, int32 * buf, int mode) {
    int stat;
    uint32 flags;
    uint32 phy_addr;
    stat = 0;
    stat = bv32_convert (addr, pgt_addr, &phy_addr, &flags);
    if (stat != 0) {
        goto error;
    }
    if (!(flags & PTE_R)) {
        stat |= E_MEM_BV32_PTE_MISSING_R;
        goto error;
    }
    stat = mem_phy_read (phy_addr, buf, mode);
    success:
    return stat;
    error:
    return stat;
}

int 
bv32_mem_virt_write (uint32 addr, uint32 pgt_addr, int32 value, int mode) {
    int stat;
    uint32 flags;
    uint32 phy_addr;
    stat = 0;
    stat = bv32_convert (addr, pgt_addr, &phy_addr, &flags);
    if (stat != 0) {
        printf ("v->p 0x%X->0x%X, stat:0x%X\n", addr, phy_addr, stat);
        getchar ();
    }
    if (stat != 0) {
        goto error;
    }
    if (!(flags & PTE_W)) {
        stat |= E_MEM_BV32_PTE_MISSING_W;
        goto error;
    }
    stat = mem_phy_write (phy_addr, value, mode);
    success:
    return stat;
    error:
    return stat;
}
